/**
  ******************************************************************************
  * @file    bootloader.c
  * @author  Puya Application Team
  * @brief   Bootloader application entry point
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 Puya Semiconductor.
  * All rights reserved.</center></h2>
  *
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "bootloader.h"
#include "usart.h"
#include "flash.h"
#include "wdg.h"

/* Private types ------------------------------------------------------------*/
typedef void (*Function_Pointer)(void);

/* Private define ------------------------------------------------------------*/
#define AREA_ERROR                        0x0U              /* Error Address Area */
#define FLASH_AREA                        0x1U              /* Flash Address Area */
#define RAM_AREA                          0x2U              /* RAM Address area */
#define OB_AREA                           0x3U              /* Option bytes Address area */
#define OTP_AREA                          0x4U              /* OTP Address area */
#define SYS_AREA                          0x5U              /* System memory area */
#define EB_AREA                           0x7U              /* Engi bytes Address area */

/* Private macro -------------------------------------------------------------*/

const uint8_t BOOTLOADER_CMD[] = {
  CMD_GET_COMMAND, CMD_GET_VERSION, CMD_GET_ID,
  CMD_READ_MEMORY, CMD_GO, CMD_WRITE_MEMORY, CMD_EXT_ERASE_MEMORY,
  CMD_WRITE_PROTECT, CMD_WRITE_UNPROTECT, CMD_READ_PROTECT, CMD_READ_UNPROTECT
};

/* Private variables ---------------------------------------------------------*/
uint8_t guc_DataBuffer[0x180];

/* Private functions --------------------------------------------------------*/
ErrorStatus ReadMemory(void);
ErrorStatus Go(void);
ErrorStatus WriteMemory(void);
ErrorStatus ExtendedErase(void);
ErrorStatus WriteProtect(void);
uint8_t GetXOR(const uint8_t* pucDataBuf, uint16_t wDataLength, uint8_t ucBase);



/* Exported functions --------------------------------------------------------*/
/**
  * @brief  Initialize Bootloader.
  * @param  None.
  * @retval None.
  */
void Bootloader_Init(void)
{
  WDG_Init();
  
  USART_Init();

  USART_SendByte(ACK_BYTE);
}

/**
  * @brief  This function is used to select which protocol will be used when communicating with the host.
  * @param  None.
  * @retval None.
  */
void Bootloader_ProtocolDetection(void)
{
  uint8_t i;
  uint8_t ucCommand;
  ErrorStatus eStatus = SUCCESS;

#if 1//获取命令
  ucCommand = USART_ReadByte();
  /* Check the data integrity */
  if ((ucCommand ^ USART_ReadByte()) != 0xFF)
  {
    USART_SendByte(NACK_BYTE);
    return;
  }
#endif

#if 0//RDP检查
  switch (ucCommand)
  {
  case CMD_WRITE_MEMORY:
  case CMD_EXT_ERASE_MEMORY:
  case CMD_READ_MEMORY:
  case CMD_GO:
  case CMD_WRITE_PROTECT:
  case CMD_WRITE_UNPROTECT:
  case CMD_READ_PROTECT:
    if (FLASH_OPTR1_RDP_LEVEL_0 != READ_BIT(FLASH->OPTR1, FLASH_OPTR1_RDP))//RDP激活？是则返回NACK
    {
      USART_SendByte(NACK_BYTE);
      return;
    }
  case CMD_GET_COMMAND:
  case CMD_GET_VERSION:
  case CMD_GET_ID:
//  case CMD_GET_DEVICE_IDCODE:
  case CMD_READ_UNPROTECT:
//  case CMD_WRITE_PERIPHERALS:
//  case CMD_READ_PERIPHERALS:
    USART_SendByte(ACK_BYTE);
    break;
  }
#else
  USART_SendByte(ACK_BYTE);
#endif

#if 1//FLASH_Unlock(统一入口，防止芯片跑飞，误操作FLASH)
  switch (ucCommand)
  {
  case CMD_WRITE_MEMORY:
  case CMD_EXT_ERASE_MEMORY:
  case CMD_WRITE_PROTECT:
  case CMD_WRITE_UNPROTECT:
  case CMD_READ_PROTECT:
  case CMD_READ_UNPROTECT:
    //检查 FLASH_SR 寄存器 BSY 位，确认没有正在进行的 flash 操作
    if (READ_BIT(FLASH->SR, (FLASH_SR_BSY0|FLASH_SR_BSY1)))
    {
      USART_SendByte(NACK_BYTE);
      return;
    }
    //向 FLASH_KEYR 寄存器依次写 KEY1 和 KEY2，解除 FLASH_CR 寄存器的保护
    if (FLASH_CR_LOCK == READ_BIT(FLASH->CR, FLASH_CR_LOCK))
    {
      WRITE_REG(FLASH->KEYR, FLASH_KEY1);
      WRITE_REG(FLASH->KEYR, FLASH_KEY2);
    }
    break;
  }
#endif

#if 1//执行命令  
  switch (ucCommand)
  {
  case CMD_GET_COMMAND:
    USART_SendByte(COUNTOF(BOOTLOADER_CMD));
    USART_SendByte(VERSION);
    for(i=0; i<COUNTOF(BOOTLOADER_CMD); i++)
    {
      USART_SendByte(BOOTLOADER_CMD[i]);
    }
    break;

  case CMD_GET_VERSION:
    USART_SendByte(VERSION);
    USART_SendByte(PID>>8);
    USART_SendByte(PID&0xFF);
    break;

  case CMD_GET_ID://RETURN ST IDCODE
    USART_SendByte(0x01);
    USART_SendByte(PID>>8);
    USART_SendByte(PID&0xFF);
    break;

//  case CMD_GET_DEVICE_IDCODE:
//    USART_SendByte(0x01);
//    USART_SendByte(DBGMCU_IDCODE>>8);
//    USART_SendByte(DBGMCU_IDCODE&0xFF);
//    break;

  case CMD_READ_MEMORY:
    eStatus = ReadMemory();
    if (SUCCESS == eStatus) {
      return;
    }
    break;

  case CMD_GO:
    eStatus = Go();
    break;

  case CMD_WRITE_MEMORY:
    eStatus = WriteMemory();
    break;

  case CMD_EXT_ERASE_MEMORY:
    eStatus = ExtendedErase();
    break;

//  case CMD_WRITE_PROTECT:
//    eStatus = WriteProtect();
//    break;

//  case CMD_WRITE_UNPROTECT:
//    guc_DataBuffer[0] = 0xFF;
//    guc_DataBuffer[1] = 0xFF;
//    guc_DataBuffer[4] = 0xFF;
//    guc_DataBuffer[5] = 0xFF;
//    eStatus = WriteOption(BANK0_WRPR_BASE, guc_DataBuffer, 0x03);
//    break;

  case CMD_READ_PROTECT:
    guc_DataBuffer[0] = FLASH_OPTR1_RDP_LEVEL_1;
    eStatus = WriteOption(OPTR1_BASE, guc_DataBuffer, 0x00);
    break;

  case CMD_READ_UNPROTECT:
    guc_DataBuffer[0] = FLASH_OPTR1_RDP_LEVEL_0;
    eStatus = WriteOption(OPTR1_BASE, guc_DataBuffer, 0x00);
    break;

#ifdef   ENABLE_PERIPHERALS
  case CMD_WRITE_PERIPHERALS:
    for (i=0; i<9; i++)
    {
      guc_DataBuffer[i] = USART_ReadByte();
    }
    if (0x55 != guc_DataBuffer[8])
    {
      eStatus = ERROR;
      break;
    }
    M32(M32(guc_DataBuffer)) = M32(guc_DataBuffer+4);
    break;

  case CMD_READ_PERIPHERALS:
    for (i=0; i<5; i++)
    {
      guc_DataBuffer[i] = USART_ReadByte();
    }
    if (0xAA != guc_DataBuffer[4])
    {
      eStatus = ERROR;
      break;
    }
    M32(guc_DataBuffer+4) = M32(M32(guc_DataBuffer+0));
    for (i=0; i<4; i++)
    {
      USART_SendByte(guc_DataBuffer[4+i]);
    }
    break;
#endif

  default:
    eStatus = ERROR;
    break;
  }
#endif

#if 1//FLASH_Lock
  switch (ucCommand)
  {
  case CMD_WRITE_MEMORY:
  case CMD_EXT_ERASE_MEMORY:
  case CMD_WRITE_PROTECT:
  case CMD_WRITE_UNPROTECT:
  case CMD_READ_PROTECT:
  case CMD_READ_UNPROTECT:
    SET_BIT(FLASH->CR, FLASH_CR_LOCK);//FLASH_Lock
    break;
  }
#endif

  USART_SendByte((SUCCESS == eStatus) ? ACK_BYTE : NACK_BYTE);
}

/* Private functions --------------------------------------------------------*/

#define SRAM_END            (0x20004000UL)
/**
  * @brief  Get the address and Check it is valid or not and returns the area type.
  * @param  pdwAddr The address to be got and checked.
  * @retval The address area: FLASH_AREA, RAM_AREA... if the address is valid
  *         or AREA_ERROR if the address is not valid.
  */
uint8_t GetAddressArea(uint32_t* pdwAddr)
{
  guc_DataBuffer[0] = USART_ReadByte();
  guc_DataBuffer[1] = USART_ReadByte();
  guc_DataBuffer[2] = USART_ReadByte();
  guc_DataBuffer[3] = USART_ReadByte();
  if (USART_ReadByte() != GetXOR(guc_DataBuffer, 0x04, 0x00))
  {
    return AREA_ERROR;
  }
  USART_SendByte(ACK_BYTE);
  *pdwAddr = (guc_DataBuffer[0]<<24)+(guc_DataBuffer[1]<<16)+(guc_DataBuffer[2]<<8)+guc_DataBuffer[3];

  if ((*pdwAddr >= FLASH_BASE) && (*pdwAddr < FLASH_END))
  {
    if (FLASH_OPTR1_RDP_LEVEL_0 != READ_BIT(FLASH->OPTR1, FLASH_OPTR1_RDP))
    {
      return AREA_ERROR;
    }
    return FLASH_AREA;
  }

  if ((*pdwAddr >= SRAM_BASE) && (*pdwAddr < SRAM_END))
  {
    return RAM_AREA;
  }
  
  if ((*pdwAddr >= FLASH_OTP_BASE) && (*pdwAddr < FLASH_OTP_END))
  {
    return OTP_AREA;
  }
  
  //if ((*pdwAddr >= OB_BASE) && (*pdwAddr < (OB_BASE+0x80)))
  if (SYSTEM_BASE == ((*pdwAddr)&SYSTEM_BASE))
  {
    return OB_AREA;
  }

  return AREA_ERROR;
}

/**
 * @brief  This function is used to read memory from the device.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: ReadMemory operation done
 *          - ERROR:   ReadMemory operation failed or the value of address is not ok
 */
ErrorStatus ReadMemory(void)
{
  uint16_t i;
  uint32_t dwAddr;

  if (AREA_ERROR == GetAddressArea(&dwAddr))
  {
    return ERROR;
  }

  guc_DataBuffer[0] = USART_ReadByte();
  if ((guc_DataBuffer[0] ^ USART_ReadByte()) != 0xFF)
  {
    return ERROR;
  }
  USART_SendByte(ACK_BYTE);

  for (i=0; i<guc_DataBuffer[0]+1; i++)
  {
    USART_SendByte(M8(dwAddr+i));
  }

  return SUCCESS;
}

/**
 * @brief  This function is used to jump to the user application.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: Go operation done
 *          - ERROR:   Go operation failed or the value of address is not ok
 */
ErrorStatus Go(void)
{
  uint8_t ucMemArea;
  uint32_t dwAddr;
  Function_Pointer jump_to_address;

  ucMemArea = GetAddressArea(&dwAddr);
  if ((ucMemArea != FLASH_AREA) && (ucMemArea != RAM_AREA))
  {
    return ERROR;
  }

  __enable_irq();

  /*名称  说明                      地址
    -      保留（实际存的是MSP地址）  0x00000000
    Reset  复位                      0x00000004
  */
  __set_MSP(M32(dwAddr));//Set Main Stack Pointer

  jump_to_address = (Function_Pointer)(M32(dwAddr + 4U));

  jump_to_address();

  return SUCCESS;
}

/**
 * @brief  This function is used to write in to device memory.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: WriteMemory operation done
 *          - ERROR:   WriteMemory operation failed or the value of address is not ok
 */
ErrorStatus WriteMemory(void)
{
  uint16_t i;
  uint8_t ucMemArea;
  uint8_t ucDataLength;
  uint32_t dwAddr;
  ErrorStatus eResultFlag;

  ucMemArea = GetAddressArea(&dwAddr);
  if (AREA_ERROR == ucMemArea)
  {
    return ERROR;
  }

  ucDataLength = USART_ReadByte();
  for (i=0; i<ucDataLength+1; i++)
  {
    guc_DataBuffer[i] = USART_ReadByte();
  }

  if (USART_ReadByte() != GetXOR(guc_DataBuffer, ucDataLength+1, ucDataLength))
  {
    return ERROR;
  }

  switch (ucMemArea)
  {
  case FLASH_AREA:
  case OTP_AREA:
    eResultFlag = WriteFlash(dwAddr, guc_DataBuffer, ucDataLength);
    break;
  case RAM_AREA:
    for (i=0; i<ucDataLength+1; i++)
    {
      M8(dwAddr+i) = guc_DataBuffer[i];
    }
    eResultFlag = SUCCESS;
    break;
  case OB_AREA:
    eResultFlag = WriteOption(dwAddr, guc_DataBuffer, ucDataLength);
    break;
  }

  return eResultFlag;
}

/**
 * @brief  This function is used to erase a memory.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: ExtendedErase operation done
 *          - ERROR:   ExtendedErase operation failed or the value of address is not ok
 */
ErrorStatus ExtendedErase(void)
{
  uint8_t i;
  uint8_t ucXOR;
  uint8_t ucDataTemp;
  uint8_t ucFuncFlag;
  uint8_t ucDataLength;
  ErrorStatus eResultFlag = ERROR;

  /* Read number of pages to be erased */
  ucFuncFlag = USART_ReadByte();
  ucDataLength = USART_ReadByte();

  /* Checksum initialization */
  ucXOR  = ucFuncFlag;
  ucXOR ^= ucDataLength;

  if (0xFF == ucFuncFlag)//0xFFFY, Y=F,E,D
  {
    //接收双字节的校验和
    if (USART_ReadByte() != ucXOR)
    {
      return ERROR;
    }

    return MassErase();//0xFFFF = 批量擦除
  }
  else
  {
    for (i=0; i<2*(ucDataLength+1); i++)
    {
      guc_DataBuffer[i] = USART_ReadByte();
    }
    ucXOR = GetXOR(guc_DataBuffer, 2*(ucDataLength+1), ucXOR);

    if (USART_ReadByte() != ucXOR)
    {
      return ERROR;
    }

    for (i=0; i<ucDataLength+1; i++)
    {
      ucDataTemp = guc_DataBuffer[2*i+0];
      guc_DataBuffer[2*i+0] = guc_DataBuffer[2*i+1];
      guc_DataBuffer[2*i+1] = ucDataTemp;
    }

    switch (ucFuncFlag)
    {
    case 0x00://兼容ST,按1KBytes擦除
      eResultFlag = PageErase((uint16_t *)guc_DataBuffer, ucDataLength, 8);
      break;
    case 0x10://PageErase
      eResultFlag = PageErase((uint16_t *)guc_DataBuffer, ucDataLength, 1);
      break;
    case 0x20://SectorErase
      eResultFlag = SectorErase((uint16_t *)guc_DataBuffer, ucDataLength);
      break;
    case 0xF0://BankErase
      eResultFlag = BankErase(guc_DataBuffer[0]);
      break;
    default:
      eResultFlag = ERROR;
      break;
    }
  }

  return eResultFlag;
}


/**
 * @brief  This function is used to enable write protect.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: WriteProtect operation done
 *          - ERROR:   WriteProtect operation failed or the value of address is not ok
 */
ErrorStatus WriteProtect(void)
{
  uint16_t i;
  uint8_t ucDataLength;
  uint32_t ProtectedPages = 0xFFFFFFFF;

  ucDataLength = USART_ReadByte();
  for (i=0; i<ucDataLength+1; i++)
  {
    guc_DataBuffer[i] = USART_ReadByte();
  }

  if (USART_ReadByte() != GetXOR(guc_DataBuffer, ucDataLength+1, ucDataLength))
  {
    return ERROR;
  }

  for (i=0; i<ucDataLength+1; i++)
  {
    CLEAR_BIT(ProtectedPages, (1U<<guc_DataBuffer[i]));
  }

  guc_DataBuffer[0] = (ProtectedPages>>0)&0xFF;
  guc_DataBuffer[1] = (ProtectedPages>>8)&0xFF;
  guc_DataBuffer[4] = (ProtectedPages>>16)&0xFF;
  guc_DataBuffer[5] = (ProtectedPages>>24)&0xFF;

  return WriteOption(BANK0_WRPR_BASE, guc_DataBuffer, 0x03);
}

/**
 * @brief  This function is used to get XOR of the DataBuf.
 * @param *pucDataBuf Pointer to the DataBuf
 * @param wDataLength The length of the DataBuf
 * @param ucBase The base value of the DataBuf
 * @retval The XOR of the DataBuf
 */
uint8_t GetXOR(const uint8_t* pucDataBuf, uint16_t wDataLength, uint8_t ucBase)
{
  while(wDataLength--)
  {
    ucBase = ucBase ^*pucDataBuf++;
  }
  return ucBase;
}

void JumpToAddress(uint32_t dwAddr)
{
  Function_Pointer jump_to_address;
  
  if (SRAM_BASE == (HW32_REG(dwAddr) & 0x2FFE0000))
  {
    __enable_irq();
    
    /*名称  说明                      地址
    -      保留（实际存的是MSP地址）0x00000000
    Reset  复位                     0x00000004
    */
    __set_MSP(HW32_REG(dwAddr));//Set Main Stack Pointer

    jump_to_address = (Function_Pointer)(HW32_REG(dwAddr + 4U));

    jump_to_address();
  }
}
/**
  * @brief  Configure system clock
  * @param  None
  * @retval None
  */
void APP_SystemClockConfig(uint32_t Value, uint32_t HCLKFrequency)
{
  /* Enable and initialize HSI */
  LL_RCC_HSI_Enable();
  LL_RCC_HSI_SetCalibFreq(Value);
  while(LL_RCC_HSI_IsReady() != 1)
  {
  }
  
  /* Configure AHB prescaler */
  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);

  /* Configure HSISYS as system clock and initialize */
  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
  {
  }
  
  /* Set APB1 & APB2 prescaler: PCLK1 = HCLK, PCLK2 = HCLK */
  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
  LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
  
//  /* Set systick to 1ms in using frequency set to 24MHz */
//  LL_Init1msTick(24000000);
  
  /* Update system clock global variable SystemCoreClock (can also be updated by calling SystemCoreClockUpdate function) */
  LL_SetSystemCoreClock(HCLKFrequency);
}

